# Copyright (c) 2006 "Gardiner"
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'ea'
require 'util'
require 'winer_files'
require 'winer_sequence'
require 'winer_stats'
require 'enumerator'

class Winer
	include EvolutionaryAlgorithm

	attr_reader :vineyards
	attr_reader :strains
	
	def initialize(vineyardFile, strainFile)
		@vineyards = WinerFiles.read_vineyards(vineyardFile)
		@strains = WinerFiles.read_strains(strainFile)

		@vineyards.freeze
		@strains.freeze
		
		@selector = EvolutionaryAlgorithm.make_fitness_proportional_selector(method(:get_fitness))
		@recombinator = method(:two_point_recombine)
		@mutator = method(:mutate_seq)
		@mutate_chance = 0.05
		
		@evaluator = StatEvaluator.new
	end
	
	def initialize_ea(vineyards = @vineyards, strains = @strains, populationSize = 300)
		@population = Array.new(populationSize) { |v| 
			v = TendingSequence.new(vineyards, rand(vineyards.length), strains, rand(strains.length)) 
		}
	end
	
	def vineyard_tourney(round, quiet, vineyards = @vineyards, strains = @strains, tourneySize = 4, generations = 20, populationSize = 300)		
		nextCall = Proc.new { |vines|
			nextVineyards = []
			
			vines.each_slice(tourneySize) { |v|
				initialize_ea(v, strains, populationSize)
				
				printf 8.chr*15 + "Round %2d:    0%", round unless quiet
				
				iterate(generations, quiet)
				
				round += 1
				
				best = @population.collect { |i| get_fitness(i) }.max_index
				nextVineyards << @population[best].vineyards[@population[best].vineyardIndex]			
			}
			
			if nextVineyards.length > 1			
				nextCall.call(nextVineyards)
			end
		}
		
		nextCall.call(vineyards)
	end
	
	def get_fitness(ind)
		ind.evaluate(@evaluator.method(:evaluate_stats))
	end
	
	def two_point_recombine(ind1, ind2)
		ind1.two_point_recombine(ind2)
		ind1
	end
	
	def mutate_seq(ind)
		ind.mutate()
		ind
	end
	
	def scoring
		@evaluator.scoring
	end
	
	def scoring=(ev)
		@evaluator.scoring = ev
	end
	
	def requirements
		@evaluator.requirements
	end
	
	def requirements=(req)
		@evaluator.requirements = req
	end
end